import * as THREE from 'three';
import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js';
import {TWEEN} from "three/examples/jsm/libs/tween.module.min";
import {GLTFLoader} from "three/examples/jsm/loaders/GLTFLoader";
import Stats from 'three/examples/jsm/libs/stats.module.js';
import {GUI} from 'three/examples/jsm/libs/dat.gui.module.js';
import {baseUrl} from '../../js/base.js'

import '../../css/base.css'

let body = document.body, canvas = document.getElementById("canvas")
let renderer, scene, camera;
let directionalLight

let stats, orbitControls

let faceGroup = new THREE.Group(), face, line

let mouse = new THREE.Vector2(), point = null;
let raycaster = new THREE.Raycaster();

const guiParams = {
    open: true,
    setUpFloor: false,
    selectFloorActive: [-1, 0, 1, 2],

}
let pointsList = [], floorName = ''

let floorParams = [] // {index, name: object.name, object, userData: object.userData}
let buildingGroup = new THREE.Group()

function initCSR() {
    const w = canvas.offsetWidth;
    const h = canvas.offsetHeight;

    camera = new THREE.PerspectiveCamera(75, w / h, 0.1, 10000)
    camera.position.set(0, 60, 100)

    window.scene = scene = new THREE.Scene()
    scene.background = new THREE.Color(0xefefef);
    // scene.fog = new THREE.Fog( 0xefefef, 100, 500 );

    renderer = new THREE.WebGLRenderer({canvas, antialias: true, alpha: false});
    renderer.outputEncoding = THREE.sRGBEncoding;
    camera.updateProjectionMatrix();
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(w, h, false)
}

function initLight() {
    const ambientLight = new THREE.AmbientLight(0x909399, .58);
    scene.add(ambientLight)

    directionalLight = new THREE.DirectionalLight(0x909399, .5);
    directionalLight.position.set(-1000, 1000, -1000)
    scene.add(directionalLight)

    // const directionalLight3 = new THREE.DirectionalLight(0x909399, .3);
    // directionalLight3.position.set(-1, 1, 1)
    // scene.add(directionalLight3)
}

function initModel() {
    const gltfLoader = new GLTFLoader();
    let textureLoader = new THREE.TextureLoader();
    gltfLoader.load(baseUrl + 'static/models/666.glb', onLoad, onProgress, onError)


    function onLoad(gltf) {
        const gltfScene = gltf.scene

        // console.log(gltfScene)

        floorParams[0] = gltfScene.children[2]
        floorParams[1] = gltfScene.children[1]
        floorParams[2] = gltfScene.children[0]

        floorParams.forEach((value, index) => {
            const texture = textureLoader.load(baseUrl + 'static/images/vrLook/O1_f.webp');
            texture.wrapS = texture.wrapT = 400
            value.material = new THREE.MeshPhongMaterial({transparent: true, color: 0x909399, map: texture})
            buildingGroup.add(value)
        })

        scene.add(buildingGroup)

        // scene.add(gltfScene)
        console.log(gltfScene)
        // gltfScene.scale.set(0.001, 0.001, 0.001)
        gltfScene.add(new THREE.AxesHelper(100))
        // setTimeout(() => {
        //     setModelCenter(gltfScene)
        // }, 1000)
        if (guiParams.open) {
            setTimeout(() => openFloor(true), 100)
        }
    }

    // scene.add(new THREE.AxesHelper(100))

    function onProgress(xhr) {
        // console.log(xhr)
    }

    function onError(err) {
        console.error(err)
    }
}

function setModelCenter(model) {
    model.updateWorldMatrix()
    const box = new THREE.Box3().setFromObject(model)
    const boxSize = box.getSize()
    const center = box.getCenter(new THREE.Vector3())
    model.translateY(-center.y)
    model.translateX(-center.x)
    model.translateZ(-center.z)
    model.position.copy(center)
    if (boxSize.x > boxSize.y) {
        camera.position.z = boxSize.x * -1
    } else {
        camera.position.z = boxSize.y * -1
    }
    camera.lookAt(0, 0, 0)
}

function initControls() {
    orbitControls = new OrbitControls(camera, canvas);
    orbitControls.enableDamping = true

    orbitControls.target.set(0, 0, 0)
}

function initHelper() {
    stats = new Stats()
    body.appendChild(stats.domElement)

    const gui = new GUI();

    gui.add(guiParams, 'open').onChange((boolean) => {
        openFloor(boolean)
    })
    gui.add(guiParams, 'setUpFloor').onChange((boolean) => {
    })
    gui.add(guiParams, 'selectFloorActive', guiParams.selectFloorActive).onChange((number) => {
        if (+number === -1) {
            floorParams.forEach(value => {
                console.log(value)
                value.visible = true
            })

            new TWEEN.Tween({position: scene.position.y}).to({
                position: 0
            }, 500).onUpdate(val => {
                scene.position.setY(val.position)
            }).start()
            return floorName = ''
        }
        floorName = number
        selectFloor(number)
    })
}

function openFloor(boolean) {
    buildingGroup.children.forEach((value, index) => {
        new TWEEN.Tween({position: value.position.y}).to({
            position: boolean ? value.position.y + value.name * 60 :
                value.position.y - value.name * 60
        }, 500).onUpdate(val => {
            value.position.setY(val.position)
        }).onComplete((val) => {
            console.log(value.name, val.position)
        }).start()
    })
}

function onWindowResize() {
    renderer.setPixelRatio(window.devicePixelRatio);
    const width = canvas.offsetWidth;
    const height = canvas.offsetHeight;
    camera.aspect = width / height;
    camera.updateProjectionMatrix();
    renderer.setSize(width, height, false);
} // 控制窗口大小

function render() {
    if (orbitControls) orbitControls.update()
    if (TWEEN) TWEEN.update()
    renderer.render(scene, camera)
    if (stats) stats.update();
}

function animation() {
    requestAnimationFrame(animation)
    render()
}

function addEL() {
    window.addEventListener('resize', onWindowResize);
    canvas.addEventListener('pointerdown', mouseDown, false)
    canvas.addEventListener('pointermove', mouseMove, false)
    canvas.addEventListener('pointerup', mouseUp, false)
    canvas.onmouseup = function (ev) {
        if (ev.button === 2) {
            floorName = ''
            line = drawLine(faceGroup, pointsList, {isLine: true})
            face = drawFace(faceGroup, pointsList)
        }
    }
}

function mouseDown(e) {
    //
    mouse.x = (e.offsetX / canvas.offsetWidth) * 2 - 1;
    mouse.y = -(e.offsetY / canvas.offsetHeight) * 2 + 1;

    //
    raycaster.setFromCamera(mouse, camera);
    console.log(buildingGroup.children)
    let intersectObjects = raycaster.intersectObject(buildingGroup, true);
    console.log(intersectObjects)
    if (!intersectObjects.length) return
    const object = intersectObjects[0]
    //
    console.log(object)

    point = object.point
    console.log(point)

    if (floorName === object.object.name) {
        pointsList.push([point.x, 0, point.z])
    }
}

function mouseMove(e) {
    //
    mouse.x = (e.offsetX / canvas.offsetWidth) * 2 - 1;
    mouse.y = -(e.offsetY / canvas.offsetHeight) * 2 + 1;

    //
    raycaster.setFromCamera(mouse, camera);
    let intersectObjects = raycaster.intersectObjects(buildingGroup.children);
    if (!intersectObjects.length) return
    const object = intersectObjects[0]
    //


    if (floorName === -1) {
        floorParams.forEach(value => {
            if (value.name !== object.object.name) {
                value.material.opacity = .8
            } else {
                value.material.opacity = 1
            }
        })
    }

    if (!floorName) return
    if (floorName === object.object.name) {
        point = object.point
        pointsList.pop()
        pointsList.push([point.x, 0, point.z])
        faceGroup.remove(line)
        line = drawLine(faceGroup, pointsList)
    }
}

function mouseUp(e) {
    //
    mouse.x = (e.offsetX / canvas.offsetWidth) * 2 - 1;
    mouse.y = -(e.offsetY / canvas.offsetHeight) * 2 + 1;

    //
    raycaster.setFromCamera(mouse, camera);
    let intersectObjects = raycaster.intersectObjects(buildingGroup.children);
    if (!intersectObjects.length) return
    const object = intersectObjects[0]
    //

    console.log(pointsList.length)

    if (!floorName) return
    if (floorName === object.object.name) {
        point = object.point
        pointsList.push([point.x, 0, point.z])
        faceGroup.remove(line)
        line = drawLine(faceGroup, pointsList)
    }


}

(() => {
    initCSR();
    initLight();
    initHelper()
    initModel();
    initControls();
    addEL()
    animation();
})();

/**
 * 画线
 * @param faceGroup 线添加到指定的组或者Object3D
 * @param pointList 线的点
 * @param options {color, transparent, opacity, side, depthTest, isLine}
 * @returns {Mesh} 线
 */
function drawLine(faceGroup, pointList, options) {
    const {color, transparent, opacity, side, depthTest, isLine} = options || {}
    const material = new THREE.LineBasicMaterial({
        color: color || 0xffFF00,
        transparent: transparent || true,
        opacity: opacity || 1,
        side: side || THREE.DoubleSide,
        depthTest: depthTest || false
    });
    //设置具有几何顶点的几何（Geometry）或缓冲区几何（BufferGeometry）设置顶点位置，看名字就知道了，一个是直接将数据保存在js里面的，另一个是保存在WebGL缓冲区内的，而且肯定保存到WebGL缓冲区内的效率更高
    const geometry = new THREE.Geometry();
    pointList.forEach(value => {
        geometry.vertices.push(new THREE.Vector3(...value));
    })
    //使用Line方法将线初始化
    const line = new THREE[isLine ? 'LineLoop' : 'Line'](geometry, material);
    faceGroup.add(line)
    return line
}

/**
 * 画面
 * @param faceGroup 面添加到指定的组或者Object3D
 * @param pointList 面的点
 * @param options {color, transparent, opacity, side, depthTest}
 * @returns {Mesh} 面
 */
function drawFace(faceGroup, pointList, options) {
    const {color, transparent, opacity, side, depthTest} = options || {}
    const geometry = new THREE.Geometry();

    pointList.forEach(points => {
        geometry.vertices.push(new THREE.Vector3(...points))
    })
    const vector3 = new THREE.Vector3();
    const c = new THREE.Color(0xffffff);
    for (let i = 2; i < pointList.length; i++) {
        geometry.faces.push(new THREE.Face3(0, i - 1, i, vector3, c, i));
    }

    const material = new THREE.MeshBasicMaterial({
        color: color || 0xff0000,
        transparent: transparent || true,
        opacity: opacity || .5,
        side: side || THREE.DoubleSide,
        depthTest: depthTest || false
    });
    face = new THREE.Mesh(geometry, material);
    faceGroup.add(face)
    return face
}

function selectFloor(number) {
    faceGroup = new THREE.Group()
    const objectByName = scene.getObjectByName(number);
    if (!objectByName) return
    scene.add(faceGroup)
    pointsList = []
    floorParams.forEach((value, index) => {
        if (number === value.name) {
            value.visible = true
            console.log('value: ', value)
            value.material.transparent = true
            value.material.opacity = 0
        } else {
            value.visible = false
        }

        new TWEEN.Tween({position: scene.position.y}).to({
            position: guiParams.open ? -number * 60 : -number * 6
        }, 500).onUpdate(val => {
            scene.position.setY(val.position)
        }).start()
    })
}
